package com.dengxian.usercenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dengxian.usercenter.common.ErrorCode;
import com.dengxian.usercenter.common.enums.TeamStatusEnum;
import com.dengxian.usercenter.exception.BusinessException;
import com.dengxian.usercenter.model.VO.TeamUserVO;
import com.dengxian.usercenter.model.VO.UserVO;
import com.dengxian.usercenter.model.domain.Team;
import com.dengxian.usercenter.model.domain.User;
import com.dengxian.usercenter.model.domain.UserTeam;
import com.dengxian.usercenter.model.dto.TeamQuery;
import com.dengxian.usercenter.model.dto.TeamJoinRequest;
import com.dengxian.usercenter.model.dto.TeamQuitRequest;
import com.dengxian.usercenter.model.dto.TeamUpdateRequest;
import com.dengxian.usercenter.service.TeamService;
import com.dengxian.usercenter.mapper.TeamMapper;
import com.dengxian.usercenter.service.UserService;
import com.dengxian.usercenter.service.UserTeamService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.sun.javafx.font.FontResource.SALT;

/**
* @author 86151
* @description 针对表【team(队伍表)】的数据库操作Service实现
* @createDate 2023-02-09 13:51:48
*/
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
    implements TeamService{

    @Resource
    private UserService userService;

    @Resource
    private UserTeamService userTeamService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public long addTeam(Team team, User loginUser) {
        //1. 请求参数是否为空？
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2. 是否登录，未登录不允许创建
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        final long userId = loginUser.getId();
        //3. 校验信息
        //  1. 队伍人数 > 1 且 <= 20
        int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if ( maxNum < 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数不满足要求");
        }
        //  2. 队伍标题 <= 20 且不为空
        String name = team.getName();
        if (StringUtils.isBlank(name) || name.length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题不满足要求");
        }
        //  3. 描述 <= 512
        String description = team.getDescription();
        if (org.apache.commons.lang3.StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长");
        }
        //  4. status 是否公开（int）不传默认为 0（公开）
        int status = Optional.ofNullable(team.getStatus()).orElse(0);
           //封装枚举类
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if (statusEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求");
        }
        //  5. 如果 status 是加密状态，一定要有密码，且密码 <= 32
        String password = team.getPassword();
        if (TeamStatusEnum.SECRET.equals(statusEnum)) {
            if (org.apache.commons.lang3.StringUtils.isBlank(password) || password.length() > 32) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码设置不正确");
            }
        }
        //加密
        String encrytPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        //  6. 超时时间 > 当前时间
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间设置需要大于当前时间");
        }
        //  7. 校验用户最多创建 5 个队伍
        // todo 有 bug，可能同时创建 100 个队伍
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long hasTeamNum = this.count(queryWrapper);
        if (hasTeamNum >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建 5 个队伍");
        }
        //一下两个插入需要都插入成功，数据库才有插入成功，所以使用事务
        //8. 插入队伍信息到队伍表
        team.setId(null);
        team.setUserId(userId);
        if (password != null) {
            team.setPassword(encrytPassword);
        }
        boolean result = this.save(team);
        Long teamId = team.getId();
        if (!result || teamId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        //9. 插入用户 => 队伍关系到关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        result = userTeamService.save(userTeam);
        if (!result) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        return teamId;
    }

    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery, boolean isAdmin) {
        //创建一个查询对象
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        //组合查询条件
        if (teamQuery != null) {
            Long id = teamQuery.getId();
            if (id != null && id > 0) {
                queryWrapper.eq("id", id);
            }
            //
            List<Long> idList = teamQuery.getIdList();
            if (CollectionUtils.isNotEmpty(idList)) {
                queryWrapper.in("id", idList);
            }
            //同时根据队伍名称和描述模糊搜索
            String searchText = teamQuery.getSearchText();
            if (org.apache.commons.lang3.StringUtils.isNotBlank(searchText)) {
                queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
            }
            //根据队伍名称进行模糊搜索
            String name = teamQuery.getName();
            if (org.apache.commons.lang3.StringUtils.isNotBlank(name)) {
                queryWrapper.like("name", name);
            }
            //根据队伍描述模糊搜索
            String description = teamQuery.getDescription();
            if (org.apache.commons.lang3.StringUtils.isNotBlank(description)) {
                queryWrapper.like("description", description);
            }
            // 查询队伍最大人数相等的
            Integer maxNum = teamQuery.getMaxNum();
            if (maxNum != null && maxNum > 0) {
                queryWrapper.eq("maxNum", maxNum);
            }
            // 根据创建人来查询
            Long userId = teamQuery.getUserId();
            if (userId != null && userId > 0) {
                queryWrapper.eq("userId", userId);
            }
            // 根据状态来查询
            Integer status = teamQuery.getStatus();
            TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
            if (statusEnum == null) {
                statusEnum = TeamStatusEnum.PUBLIC;
            }
            //如果不是管理员且队伍状态是私密的，返回异常
            if (!isAdmin && statusEnum.equals(TeamStatusEnum.PRIVATE)) {
                throw new BusinessException(ErrorCode.NO_AUTH);
            }
            queryWrapper.eq("status", statusEnum.getValue());
        }
        // 不展示已过期的队伍
        // expireTime is null or expireTime > now()
        queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> teamList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
        //返回给前端的完整的队伍用户列表信息
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        //todo 还可以关联查询已加入队伍的所有用户的信息，可用sql语句实现
        //关联查询创建人的用户信息（将user的信息给team）
        for (Team team : teamList) {
            //获取user信息
            Long userId = team.getUserId();
            if (userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            //类型强转，将team的属性给teamUserVO
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            // 脱敏用户信息
            if (user != null) {
                //类型强转，将user的属性给userVO
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                //将用户信息放到队伍信息中
                teamUserVO.setCreateUser(userVO);
            }
            teamUserVOList.add(teamUserVO);
        }
        return teamUserVOList;

    }

    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        //判断请求参数是否为空a
        if (teamUpdateRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long id = teamUpdateRequest.getId();
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //查询队伍是否存在
        Team oldTeam = this.getById(id);
        if (oldTeam == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        // 只有管理员或者队伍的创建者可以修改
        if (!(oldTeam.getUserId().equals(loginUser.getId())) && (!(userService.isAdmin(loginUser)))) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //如果队伍状态改为加密，必须要有密码
        /*
        如果队伍状态现在是加密，队伍之前如果不是加密状态的话
        (就没有设置密码即现在就一定需要输入密码否则抛出异常)
        所以就判断是否输入密码，没有就异常
        */
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(teamUpdateRequest.getStatus());
        String password = teamUpdateRequest.getPassword();
        if (statusEnum.equals(TeamStatusEnum.SECRET)) {
            TeamStatusEnum oldTeamstatusEnum = TeamStatusEnum.getEnumByValue(oldTeam.getStatus());
            if (!oldTeamstatusEnum.equals(TeamStatusEnum.SECRET)) {
                if (org.apache.commons.lang3.StringUtils.isBlank(password)) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密房间必须要设置密码");
                }
            }
        }
        //todo 如果用户传入的新值和老值一致，就不用 update 了(降低数据库使用次数)
        //更新队伍信息
        Team updateTeam = new Team();
        //加密
        String encrytPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        if (password != null) {
            updateTeam.setPassword(encrytPassword);
        }
        BeanUtils.copyProperties(teamUpdateRequest, updateTeam);
        return this.updateById(updateTeam);
    }

    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        //校验请求参数
        if (teamJoinRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //校验队伍是否存在
        Long teamId = teamJoinRequest.getTeamId();
        if (teamId == null || teamId < 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        //判断队伍是否过期
        Date expireTime = team.getExpireTime();
        if (expireTime != null && expireTime.before(new Date())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已过期");
        }
        // 禁止加入私有的队伍
        Integer status = team.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
        if (TeamStatusEnum.PRIVATE.equals(teamStatusEnum)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "禁止加入私有队伍");
        }
        // 如果加入的队伍是加密的，必须密码匹配才可以
        String password = teamJoinRequest.getPassword();
        //加密
        String encrytPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
            if (StringUtils.isBlank(password) || !encrytPassword.equals(team.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
            }
        }
        //该用户已加入的队伍数量
        long userId = loginUser.getId();
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("userId", userId);
        long hasJoinNum = userTeamService.count(userTeamQueryWrapper);
        // 用户最多加入 5 个队伍
        if (hasJoinNum > 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多创建和加入五个队伍");
        }
        //不能重复加入已加入的队伍（幂等性）
        userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("userId", userId);
        userTeamQueryWrapper.eq("teamId", teamId);
        long hasUserJoinTeam = userTeamService.count(userTeamQueryWrapper);
        if (hasUserJoinTeam > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户已加入该队伍");

        }
        // 只能加入未满的队伍
        QueryWrapper<UserTeam> userTeamQueryWrapper1 = new QueryWrapper<>();
        userTeamQueryWrapper1.eq("teamId",teamId);
        //已经加入队伍的人数
        long teamHasJoinNum = userTeamService.count(userTeamQueryWrapper1);
        if (teamHasJoinNum >= team.getMaxNum()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该队伍人数已满");
        }
        // 新增队伍 - 用户关联信息
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        return userTeamService.save(userTeam);
    }

    @Override
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        // 校验请求参数
        if (teamQuitRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 校验队伍是否存在
        Long teamId = teamQuitRequest.getTeamId();
        if (teamId == null || teamId < 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        //校验是否已加入队伍
        long userId = loginUser.getId();
        UserTeam queryUserTeam = new UserTeam();
        queryUserTeam.setTeamId(teamId);
        queryUserTeam.setUserId(userId);
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>(queryUserTeam);
        long count = userTeamService.count(queryWrapper);
        if (count == 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "未加入队伍");
        }
        //获取队伍当前加入总人数
        QueryWrapper<UserTeam> userTeamQueryWrapper1 = new QueryWrapper<>();
        userTeamQueryWrapper1.eq("teamId",teamId);
        long teamHasJoinNum = userTeamService.count(userTeamQueryWrapper1);
        //只剩一人，队伍解散
        if (teamHasJoinNum == 1) {
            this.removeById(teamId);
        } else {
            // 如果是队长退出队伍，权限转移给第二早加入的用户
            if (team.getUserId() == userId) {
                //查询已加入队伍的所有用户和获得第二早加入用户
                QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                userTeamQueryWrapper.eq("teamId", teamId);
                userTeamQueryWrapper.last("order by id asc limit 2");
                List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
                if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                UserTeam nextUserTeam = userTeamList.get(1);
                Long nextTeamLeaderId = nextUserTeam.getUserId();
                //更新当前队伍的队长
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                updateTeam.setUserId(nextTeamLeaderId);
                boolean result = this.updateById(updateTeam);
                if (!result) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队伍队长失败");
                }
            }
        }
        //移除关系
        return userTeamService.remove(queryWrapper);
    }

    @Override
    public boolean deleteTeam(long id, User loginUser) {
        // 校验队伍是否存在
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(id);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        // 校验你是不是队伍的队长
        if (!team.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH, "无访问权限");
        }
        // 移除所有加入队伍的关联信息
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        long teamId = team.getId();
        userTeamQueryWrapper.eq("teamId", teamId);
        boolean result = userTeamService.remove(userTeamQueryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除队伍关联信息失败");
        }
        // 删除队伍表中的队伍信息
        return this.removeById(teamId);
    }

    @Override
    public TeamUserVO getTeamUserById(long id, User loginUser) {
        Team team = this.getById(id);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        //类型强转，将team的属性给teamUserVO
        TeamUserVO teamUserVO = new TeamUserVO();
        BeanUtils.copyProperties(team, teamUserVO);
        //获取当前队伍的加入人数
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId", id);
        long teamHasJoinNum = userTeamService.count(userTeamQueryWrapper);
        teamUserVO.setHasJoinNum((int)teamHasJoinNum);
        //判断当前用户是否加入队伍
        userTeamQueryWrapper.eq("userId", loginUser.getId());
        if (userTeamService.count(userTeamQueryWrapper) > 0) {
            teamUserVO.setHasJoin(true);
        }else {
            teamUserVO.setHasJoin(false);
        }
        //给每个teamUserVO增加该队伍全部人员信息
//        List<UserVO> userVOList = new ArrayList<>();
        //查出加入该队伍的队伍用户关系
        QueryWrapper<UserTeam> userTeamJoinQueryWrapper = new QueryWrapper<>();
        userTeamJoinQueryWrapper.eq("teamId", id);
        List<UserTeam> userTeamList = userTeamService.list(userTeamJoinQueryWrapper);
        //获取用户id列表
        List<Long> userIdUserTeamList = userTeamList.stream().map(UserTeam::getUserId).collect(Collectors.toList());
        //根据用户id列表获取用户信息列表
        List<User> userList = userService.listByIds(userIdUserTeamList);
//        BeanUtils.copyProperties(userList, userVOList);
        teamUserVO.setUserVOList(userList);
        return teamUserVO;
    }


}




